﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Linq;
using NHib = NHibernate;
using System.Linq.Expressions;

namespace Fx.Data.NHibernate
{
    public class ContextObject : ContextObject<int>
    {
    }

    public class ContextObject<TId>
    {
        #region Properties

        public virtual ISession Session
        {
            get
            {
                string factoryKey = SessionAttribute.GetKeyFrom(this);
                return SessionConfigurer.GetSessionBy(factoryKey);
            }
        }

        #endregion

        #region Public methods

        public virtual T Get<T>(TId id)
        {
            return Session.Get<T>(id);
        }

        public virtual T Find<T>(IDictionary<string, object> propertyValuePairs)
        {
            IList<T> foundList = List<T>(propertyValuePairs);

            if (foundList.Count > 1)
            {
                throw new NonUniqueResultException(foundList.Count);
            }
            else if (foundList.Count == 1)
            {
                return foundList[0];
            }

            return default(T);
        }

        public virtual T Save<T>(T entity)
        {
            Session.Save(entity);
            return entity;
        }

        public virtual T SaveOrUpdate<T>(T entity)
        {
            Session.SaveOrUpdate(entity);
            return entity;
        }

        public virtual T Update<T>(T entity)
        {
            Session.Update(entity);
            return entity;
        }

        public virtual IQueryable<T> Queryable<T>()
        {
            return Session.Query<T>();
        }

        public virtual void Delete(object entity)
        {
            Session.Delete(entity);
        }

        public virtual IList<T> List<T>()
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));
            return criteria.List<T>();
        }

        public virtual IList<T> List<T>(IDictionary<string, object> propertyValuePairs)
        {
            var criteria = Session.CreateCriteria(typeof(T));

            foreach (string key in propertyValuePairs.Keys)
            {
                if (propertyValuePairs[key] != null)
                {
                    criteria.Add(NHib.Criterion.Expression.Eq(key, propertyValuePairs[key]));
                }
                else
                {
                    criteria.Add(NHib.Criterion.Expression.IsNull(key));
                }
            }

            return criteria.List<T>();
        }

        public virtual IList<T> ListPage<T>(PageItem pageItem, IList<Expression<Func<T, bool>>> whereFunctions) 
            where T : class, new()
        {
            return ListPage<T>(pageItem, whereFunctions, null);
        }

        public virtual IList<T> ListPage<T>(PageItem pageItem, IList<Expression<Func<T, bool>>> whereFunctions, IList<Expression<Func<T, bool>>> orderFunctions) 
            where T : class, new()
        {
            var skipSize = pageItem.PageSize * (pageItem.PageIndex - 1);
            var pageSize = pageItem.PageSize;
            var queryable = Session.Query<T>();
            if (whereFunctions != null)
            {
                foreach (var whereFunc in whereFunctions)
                {
                    queryable = queryable.Where(whereFunc);
                }
            }
            if (orderFunctions != null)
            {
                foreach (var orderFunc in orderFunctions)
                {
                    queryable = queryable.OrderBy(orderFunc);
                }
            }
            pageItem.TotalRows = queryable.Count();
            return queryable.ToList();
        }

        #endregion

        #region Transaction methods

        public void CommitChanges()
        {
            Session.Flush();
        }

        public void BeginTransaction()
        {
            Session.BeginTransaction();
        }

        public void CommitTransaction()
        {
            Session.Transaction.Commit();
        }

        public void RollbackTransaction()
        {
            Session.Transaction.Rollback();
        }

        public bool TransactionActive
        {
            get { return Session.Transaction.IsActive; }
        }

        #endregion
    }
}
